package com.caishi.lkx.exam.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.caishi.lkx.common.AdminBaseApi;
import com.caishi.lkx.common.role.Roles;
import com.caishi.lkx.exam.model.CategoryModel;
import com.caishi.lkx.exam.model.ExamCalendarModel;
import com.caishi.lkx.exam.model.ExamModel;
import com.caishi.lkx.exam.service.ICategoryService;
import com.caishi.lkx.exam.service.IColumnService;
import com.caishi.lkx.exam.service.IExamService;
import com.caishi.lkx.exam.service.IIndustryService;
import com.caishi.lkx.exam.service.impl.ExamCalendarServiceImpl;
import com.caishi.lkx.exam.vo.IndexExamCalendarVo;
import com.caishi.lkx.user.service.IUserService;
import com.zzw.common.Wrappers;
import com.zzw.common.annotation.RateLimiterApi;
import com.zzw.common.entity.impl.BaseEntity;
import com.zzw.common.gateway.limit.RateLimiterApiTarget;
import com.zzw.common.qps.RejectStrategy;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jodd.util.StringUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author by liyihua
 * @date : 2022-01-06 14:29
 */
@Tag(name = "考试日历相关接口")
@RestController
@RequestMapping("/exam/calendar")
public class ExamCalendarApi extends AdminBaseApi<ExamCalendarServiceImpl, ExamCalendarModel, String> {
    @Resource
    private ExamCalendarServiceImpl examCalendarService;
    @Resource
    private IIndustryService industryService;
    @Resource
    private IUserService userService;
    @Resource
    private IExamService examService;
    @Resource
    private ICategoryService categoryService;
    @Resource
    private IColumnService columnService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public ExamCalendarServiceImpl loadService() {
        return examCalendarService;
    }

    @Override
    protected LambdaQueryWrapper<ExamCalendarModel> baseWrapper(ExamCalendarModel model, JSONObject otherParam) {
        var st = otherParam.get("startTime");
        var ed = otherParam.get("endTime");
        if (StringUtil.isNotBlank(model.getExamName())) {
            List<String> examIds = examService.selectList(Wrappers.<ExamModel>lambdaQuery()
                            .like(ExamModel::getName, model.getExamName()))
                    .stream().map(BaseEntity::getId).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(examIds)) {
                return super.baseWrapper(model.clearEmptyStringField("examName"), otherParam)
                        .in(ExamCalendarModel::getExamId, examIds)
                        .ge(st != null, ExamCalendarModel::getExamDate, st)
                        .le(ed != null, ExamCalendarModel::getExamDate, ed);
            }
            return Wrappers.<ExamCalendarModel>lambdaQuery().isNull(BaseEntity::getId)
                    .ge(st != null, ExamCalendarModel::getExamDate, st)
                    .le(ed != null, ExamCalendarModel::getExamDate, ed);
        }
        return super.baseWrapper(model.clearEmptyStringField("examName"), otherParam)
                .ge(st != null, ExamCalendarModel::getExamDate, st)
                .le(ed != null, ExamCalendarModel::getExamDate, ed);
    }

    @Override
    protected List<ExamCalendarModel> listCover(List<ExamCalendarModel> result, JSONObject otherParam) {
        return result.parallelStream().peek(r -> {
            r.setIndustryName(industryService.getById(r.getIndustryId()).getName());
            r.setModifyName(r.getModifyBy() != null ? userService.getUserNameById(r.getModifyBy()) : null);
            r.setExamName(examService.getById(r.getExamId()) != null ? examService.getById(r.getExamId()).getName() : null);
            r.setCategoryName(categoryService.getById(r.getCategoryId()) != null ? categoryService.getById(r.getCategoryId()).getName() : null);
        }).collect(Collectors.toList());

    }


    @GetMapping("get-calendar-list")
    @Operation(summary = "考试日历查询")
    @Parameters({@Parameter(name = "examId", required = false, description = "考试id"),
            @Parameter(name = "industryId", required = false, description = "行业id"),
            @Parameter(name = "year", required = true, description = "年"),
            @Parameter(name = "month", required = true, description = "月")})
    @Roles
    public List<ExamCalendarModel> getCalendarList(@RequestParam(required = false) String examId, @RequestParam(required = false) String industryId, @RequestParam Integer year, @RequestParam Integer month) {
        LocalDate startDate = LocalDate.of(year, month, 1);
        int monthEndDay = startDate.lengthOfMonth();
        LocalDate endDate = startDate.withDayOfMonth(monthEndDay);
        List<ExamCalendarModel> examCalendarModels = examCalendarService.selectList(Wrappers.<ExamCalendarModel>lambdaQuery()
                .eq(StringUtil.isNotBlank(examId), ExamCalendarModel::getExamId, examId).eq(StringUtil.isNotBlank(industryId), ExamCalendarModel::getIndustryId, industryId)
                .ge(ExamCalendarModel::getExamDate, startDate).le(ExamCalendarModel::getExamDate, endDate)
                .orderByAsc(ExamCalendarModel::getExamDate)
        );
        Map<String, ExamCalendarModel> collect = examCalendarModels.parallelStream().collect(Collectors.toMap(ExamCalendarModel::getExamId, v -> v, (v1, v2) -> v1.getRegistrationDate() != null ? v1 : v2));
        List<ExamCalendarModel> models = collect.values().parallelStream().sorted(Comparator.comparing(ExamCalendarModel::getExamDate)).toList();
        return listCover(models, new JSONObject());
    }

    @GetMapping("latest-date")
    @Operation(summary = "根据考试id获取最近的考试时间")
    @Roles
    public ExamCalendarModel latestDate(@RequestParam String examId) {
        ExamCalendarModel examCalendarModel = this.loadService().selectFirst(Wrappers.<ExamCalendarModel>lambdaQuery().eq(ExamCalendarModel::getExamId, examId).ge(ExamCalendarModel::getExamDate, LocalDate.now()).orderByAsc(ExamCalendarModel::getExamDate));
        if (null == examCalendarModel) {
            examCalendarModel = new ExamCalendarModel();
            examCalendarModel.setExamDate(null);
        } else {
            examCalendarModel.setIndustryName(industryService.queryName(examCalendarModel.getIndustryId()));
            examCalendarModel.setCategoryName(categoryService.queryName(examCalendarModel.getCategoryId()));
        }
        examCalendarModel.setExamName(examService.queryName(examId));
        return examCalendarModel;
    }

    @GetMapping("get-countdown-by-exam")
    @Operation(summary = "考试倒计时")
    @Parameters({
            @Parameter(name = "examId", description = "考试id"),
            @Parameter(name = "categoryId", description = "科目id"),
    })
    @Roles
    @RateLimiterApi(namespace = "exam:safe:", maxRate = 2, recoveryCount = 2, target = RateLimiterApiTarget.user, rejectStrategy = RejectStrategy.wait, bean = "memoryRateLimiterBean")
    public IndexExamCalendarVo getCountdownByExam(@RequestParam String examId, Long categoryId) {
        ExamCalendarModel model;
        String key = "countdownByExam" + ":" + examId + ":" + categoryId;
        Object o = redisTemplate.opsForValue().get(key);
        if (null != o) {
            return (IndexExamCalendarVo) o;
        }
        if (categoryId == null) {
            model = examCalendarService.getExamLately(examId);
        } else {
            model = examCalendarService.selectFirst(examCalendarService.wrappers()
                    .eq(ExamCalendarModel::getExamId, examId)
                    .eq(ExamCalendarModel::getCategoryId, categoryId)
            );
        }
        if (model == null) {
            model = new ExamCalendarModel();
            model.setExamId(examId);
        }
        IndexExamCalendarVo indexExamCalendarVo = examCalendarService.tansClientVo(model, true);
        redisTemplate.opsForValue().set(key, indexExamCalendarVo, 20, TimeUnit.MINUTES);
        return indexExamCalendarVo;
    }

    @PostMapping("/update")
    @Override
    public ExamCalendarModel add(@RequestBody ExamCalendarModel entity) {
        ExamCalendarModel examCalendarModel = loadService().insert(entity);
        if (null != examCalendarModel.getCategoryId()) {
            CategoryModel categoryModel = categoryService.getById(examCalendarModel.getCategoryId());
            examCalendarModel.setCategoryName(categoryModel != null ? categoryModel.getName() : null);
        }
        return examCalendarModel;
    }

    @PutMapping("/update")
    @Override
    public ExamCalendarModel update(@RequestBody ExamCalendarModel entity) {
        ExamCalendarModel examCalendarModel = loadService().simpleUpdate(entity);
        if (null != examCalendarModel.getCategoryId()) {
            CategoryModel categoryModel = categoryService.getById(examCalendarModel.getCategoryId());
            examCalendarModel.setCategoryName(categoryModel != null ? categoryModel.getName() : null);
        }
        return examCalendarModel;
    }

    @PostMapping("/updateExamCalendars")
    public List<ExamCalendarModel> addExamCalendars(@RequestBody ExamCalendarModel entity) {
        List<Long> categoryIds = entity.getCategoryIds();
        List<ExamCalendarModel> examCalendarModels = new ArrayList<>();
        if (!categoryIds.isEmpty()) {
            categoryIds.parallelStream().forEach(e -> {
                ExamCalendarModel model = new ExamCalendarModel();
                BeanUtils.copyProperties(entity, model);
                CategoryModel categoryModel = categoryService.getById(e);
                model.setCategoryName(categoryModel != null ? categoryModel.getName() : null);
                model.setCategoryId(e);
                examCalendarModels.add(loadService().insert(model));
            });
        } else {
            examCalendarModels.add(loadService().insert(entity));
        }
        return examCalendarModels;
    }
}

